 /*******************************************************************************
  * Copyright (c) 2005 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.osgi.framework.internal.core;

 import java.security.Permission ;
 import java.security.PermissionCollection ;
 import java.util.Enumeration ;
 import java.util.Vector ;
 import org.osgi.framework.FrameworkEvent;
 import org.osgi.service.condpermadmin.Condition;
 import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;

 /**
  *
  * This class manages the Permissions for a given code source. It tracks the
  * permissions that have yet to be satisfied as well as conditions that are
  * already satisfied.
  */
 public class ConditionalPermissions extends PermissionCollection {
     private static final long serialVersionUID = 3907215965749000496L;
     private AbstractBundle bundle;
     /**
      * This is the list of satisfiedCPIs that we are waiting to process in bulk
      * when evaluating the satisfied permissions. Elements are of type
      * ConditionalPermissionInfoImpl.
      */
     private Vector satisfiedCPIs = new Vector ();
     /**
      * This is set contains that ConditionalPermissionInfos that are satisfied
      * and immutable.
      */
     private ConditionalPermissionSet satisfiedCPS;
     /**
      * These are the CPIs that may match this CodeSource. Elements are of type
      * ConditionalPermissionSet.
      */
     private Vector satisfiableCPSs = new Vector ();
     private boolean empty;

     /**
      * Constructs a ConditionalPermission for the given bundle.
      *
      * @param bundle the bundle for which this ConditionalPermission tracks Permissions.
      */
     public ConditionalPermissions(AbstractBundle bundle, ConditionalPermissionAdmin cpa) {
         this.bundle = bundle;
         satisfiedCPS = new ConditionalPermissionSet(bundle, ConditionalPermissionAdminImpl.EMPTY_COND_PERM_INFO, ConditionalPermissionAdminImpl.EMPTY_COND);
         Enumeration en = cpa.getConditionalPermissionInfos();
         while (en.hasMoreElements()) {
             ConditionalPermissionInfoImpl cpi = (ConditionalPermissionInfoImpl) en.nextElement();
             checkConditionalPermissionInfo(cpi);
         }
     }

     void checkConditionalPermissionInfo(ConditionalPermissionInfoImpl cpi) {
         try {
             // first remove the cpi incase of an update
 removeCPI(cpi);
             Condition conds[] = cpi.getConditions(bundle);
             if (conds == null) {
                 /* Couldn't process the conditions, so we can't use them */
                 return;
             }
             boolean satisfied = true;
             for (int i = 0; i < conds.length; i++) {
                 Condition cond = conds[i];
                 if (cond.isMutable()) {
                     satisfied = false;
                 } else if (!cond.isSatisfied()) {// Note: the RFC says if !mutable, evaluated must be true
 /*
                      * We can just dump here since we have an immutable and
                      * unsatisfied condition.
                      */
                     return;
                 } else {
                     conds[i] = null; /* We can remove satisfied conditions */
                 }
             }
             if (satisfied) {
                 satisfiedCPIs.add(cpi);
             } else {
                 satisfiableCPSs.add(new ConditionalPermissionSet(bundle, new ConditionalPermissionInfoImpl[] {cpi}, conds));
             }
         } catch (Exception e) {
             bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
         }
     }

     private void removeCPI(ConditionalPermissionInfoImpl cpi) {
         satisfiedCPIs.remove(cpi);
         satisfiedCPS.remove(cpi);
         ConditionalPermissionSet cpsArray[] = (ConditionalPermissionSet[]) satisfiableCPSs.toArray(new ConditionalPermissionSet[0]);
         for (int i = 0; i < cpsArray.length; i++)
             if (cpsArray[i].remove(cpi))
                 satisfiableCPSs.remove(cpsArray[i]);
     }

     /**
      * This method is not implemented since this PermissionCollection should
      * only be used by the ConditionalPolicy which never calls this method.
      *
      * @see java.security.PermissionCollection#elements()
      */
     public void add(Permission perm) {
         // do nothing
 }

     public boolean implies(Permission perm) {
         processPending();
         boolean newEmpty = !satisfiedCPS.isNonEmpty();
         if (!newEmpty && satisfiedCPS.implies(perm)) {
             this.empty = false;
             return true;
         }
         boolean satisfied = false;
         Vector unevalCondsSets = null;
         SecurityManager sm = System.getSecurityManager();
         FrameworkSecurityManager fsm = null;
         if (sm instanceof FrameworkSecurityManager)
             fsm = (FrameworkSecurityManager) sm;
         ConditionalPermissionSet cpsArray[] = (ConditionalPermissionSet[]) satisfiableCPSs.toArray(new ConditionalPermissionSet[0]);
         cpsLoop: for (int i = 0; i < cpsArray.length; i++) {
             if (cpsArray[i].isNonEmpty()) {
                 newEmpty = false;
                 Condition conds[] = cpsArray[i].getNeededConditions();
                 if (conds == null)
                     continue;
                 // check mutable !isPostponed conditions first;
 // note that !mutable conditions have already been evaluated
 for (int j = 0; j < conds.length; j++)
                     if (conds[j] != null && !conds[j].isPostponed() && !conds[j].isSatisfied())
                         continue cpsLoop;
                 // check the implies now
 if (cpsArray[i].implies(perm)) {
                     // the permission is implied; the only unevaluated conditions left are mutable postponed conditions
 // postpone their evaluation until the end
 Vector unevaluatedConds = null;
                     for (int j = 0; j < conds.length; j++) {
                         if (conds[j] != null && conds[j].isPostponed()) {
                             if (fsm == null) {
                                 // If there is no FrameworkSecurityManager, we must evaluate now
 if (!conds[j].isSatisfied())
                                     continue cpsLoop;
                             } else {
                                 if (unevaluatedConds == null)
                                     unevaluatedConds = new Vector ();
                                 unevaluatedConds.add(conds[j]);
                             }
                         }
                     }
                     if (unevaluatedConds == null) {
                         // no postponed conditions exist return true now
 this.empty = false;
                         return true;
                     }
                     if (unevalCondsSets == null)
                         unevalCondsSets = new Vector (2);
                     unevalCondsSets.add(unevaluatedConds.toArray(new Condition[unevaluatedConds.size()]));
                     satisfied = true;
                 }
             } else {
                 satisfiableCPSs.remove(cpsArray[i]);
             }
         }
         this.empty = newEmpty;
         if (satisfied && fsm != null) {
             // There must be at least one set of Conditions to evaluate since
 // we didn't return right we we realized the permission was satisfied
 // so unevalCondsSets must be non-null.
 Condition[][] condArray = (Condition[][]) unevalCondsSets.toArray(new Condition[unevalCondsSets.size()][]);
             satisfied = fsm.addConditionsForDomain(condArray);
         }
         return satisfied;
     }

     /**
      * Process any satisfiedCPIs that have been added.
      */
     private void processPending() {
         if (satisfiedCPIs.size() > 0) {
             synchronized (satisfiedCPIs) {
                 for (int i = 0; i < satisfiedCPIs.size(); i++) {
                     ConditionalPermissionInfoImpl cpi = (ConditionalPermissionInfoImpl) satisfiedCPIs.get(i);
                     if (!cpi.isDeleted())
                         satisfiedCPS.addConditionalPermissionInfo(cpi);
                 }
                 satisfiedCPIs.clear();
             }
         }
     }

     /**
      * This method is not implemented since this PermissionCollection should
      * only be used by the ConditionalPolicy which never calls this method.
      *
      * @return always returns null.
      *
      * @see java.security.PermissionCollection#elements()
      */
     public Enumeration elements() {
         return null;
     }

     /**
      * This method returns true if there are no ConditionalPermissionInfos in
      * this PermissionCollection. The empty condition is only checked during the
      * implies method, it should only be checked after implies has been called.
      *
      * @return false if there are any Conditions that may provide permissions to
      * this bundle.
      */
     boolean isEmpty() {
         return empty;
     }

     void unresolvePermissions() {
         satisfiedCPS.unresolvePermissions();
         synchronized (satisfiableCPSs) {
             Enumeration en = satisfiableCPSs.elements();
             while (en.hasMoreElements()) {
                 ConditionalPermissionSet cs = (ConditionalPermissionSet) en.nextElement();
                 cs.unresolvePermissions();
             }
         }
     }
 }

